package app;

import exceptions.SemanticoException;
import exceptions.SintaticoException;
import interfaces.GrammarSymbols;
import java.util.LinkedList;
import java.util.Stack;

/**
 * Classe em que se aplicara' as funcoes do algoritmo:
 * Recursive Descendent Parser
 *
 * @author Jailton Maciel
 * @author Julio Cesar
 * @date 27 de Abril de 2010
 */
public class GrammarFunctions implements GrammarSymbols, Cloneable {

    private LinkedList<Token> tabelaLexica;
    private Stack<Variavel> pilhaSemantica;
    private Stack<Variavel> pct;
    private boolean ignoraID, isBoolean;
    private int tipo, operacao;
    private int linhaAtual;

    public GrammarFunctions(LinkedList<Token> tabelaLexica) throws SintaticoException, SemanticoException {
        this.tabelaLexica = tabelaLexica;
        pilhaSemantica = new Stack<Variavel>();
        pct = new Stack<Variavel>();
        ignoraID = false;
        tipo = 0;
        operacao = 0;
        isBoolean = false;
        linhaAtual = 1;
        
        programa();
    }


    /**
     * Gera um Token a partir do codigo especificado.
     *
     * @param code  Codigo do token.
     * @return      instancia de Token.
     */
    private Token genToken(int code){
        return new Token(code);
    }


    /**
     * Processa/consome o token corrente na tabela lexica, verificando se casa
     * com o token atual da analise sintatica.
     *
     * @param tokenCode             Codigo do token.
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void consume(int tokenCode) throws SintaticoException, SemanticoException {
        Token token = genToken(tokenCode);

        if(tokenCode == Code.ID && !ignoraID) {
            
            for(int i = pilhaSemantica.size()-1; i >= 0; --i) {
                if(pilhaSemantica.get(i).getId().equals("$")) {
                    break;
                }
                else {
                    if(pilhaSemantica.get(i).getId().equals(tabelaLexica.getFirst().getSimbol())) {
                        throw new SemanticoException(tabelaLexica.getFirst().getSimbol(), SemanticoException.ID_DUPLICADO, linhaAtual);
                    }         
                }
            }

            pilhaSemantica.push(new Variavel(tabelaLexica.getFirst().getSimbol(), tipo));
        }

        if(tokenCode == Code.BOOLEAN) {
            for(int i = pilhaSemantica.size()-1; i >= 0; --i) {
                if(pilhaSemantica.get(i).getTipo() != -1) {
                    break;
                }
                else {
                    pilhaSemantica.get(i).setTipo(Code.BOOLEAN);
                }
            }
            tipo = 0;
        }
        if(tokenCode == Code.INTEGER) {
            for(int i = pilhaSemantica.size()-1; i >= 0; --i) {
                if(pilhaSemantica.get(i).getTipo() != -1) {
                    break;
                }
                else {
                    pilhaSemantica.get(i).setTipo(Code.INTEGER);
                }
            }
            tipo = 0;
        }
        //if(tokenCode == Code.PROCEDURE) tipo = 0;
        if(tokenCode == Code.REAL) {
            for(int i = pilhaSemantica.size()-1; i >= 0; --i) {
                if(pilhaSemantica.get(i).getTipo() != -1) {
                    break;
                }
                else {
                    pilhaSemantica.get(i).setTipo(Code.REAL);
                }
            }
            tipo = 0;
        }

        if(tabelaLexica.size() > 0) {
            Token current = tabelaLexica.getFirst();
            linhaAtual = current.getLine();
            System.out.println("Token: " + token.getSimbol() + " - " +
                    "Lista: " + tabelaLexica.getFirst().getSimbol());

            if(token.getCode() == current.getCode()){
                tabelaLexica.removeFirst();
            }
            else{
                throw new SintaticoException(current, current.getLine());
            }
        }

        else {
            throw new SintaticoException(token, linhaAtual);
        }

    }


    /**
     * Representa o nao-terminal programa.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void programa() throws SintaticoException, SemanticoException {
        pilhaSemantica.push(new Variavel("$", 0));
        consume(Code.PROGRAM);

        consume(Code.ID);
        consume(Code.PONTO_E_VIRGULA);     
        declaracoesVariaveis();
        declaracoesDeSubprogramas();
        comandoComposto();  
        consume(Code.PONTO_FINAL);
    }      


    /**
     * Representa o nao-terminal declaracoesVariaveis.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void declaracoesVariaveis() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case(Code.VAR):
                consume(Code.VAR);
                listaDeclaracoesVariaveis();
                break;
            case(Code.PROCEDURE):
                break;
            case(Code.BEGIN):
                break;
        }
    }


    /**
     * Representa o nao-terminal listaDeclaracoesVariaveis.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeclaracoesVariaveis() throws SintaticoException, SemanticoException {
        do {
            tipo = -1;
            listaDeIdentificadores();
            consume(Code.DOIS_PONTOS);
            tipo();
            consume(Code.PONTO_E_VIRGULA);
        } while(Code.PROCEDURE != tabelaLexica.getFirst().getCode() &&
                Code.BEGIN != tabelaLexica.getFirst().getCode());
    }


    /**
     * Representa o nao-terminal listaDeIdentificadores.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeIdentificadores() throws SintaticoException, SemanticoException {
        consume(Code.ID);
        listaDeIdentificadores2();
    }


    /**
     * Representa o nao-terminal listaDeIdentificadores2.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeIdentificadores2() throws SintaticoException, SemanticoException {
       while(Code.DOIS_PONTOS != tabelaLexica.getFirst().getCode()) {
          consume(Code.VIRGULA);
          consume(Code.ID);
       }
    }


    /**
     * Representa o nao-terminal tipo.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void tipo() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.INTEGER: 
                consume(Code.INTEGER);
                break;
            case Code.BOOLEAN: 
                consume(Code.BOOLEAN);
                break;
            case Code.REAL:
                consume(Code.REAL);
                break;
        }
    }


    /**
     * Representa o nao-terminal declaracoesDeSubprogramas.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void declaracoesDeSubprogramas() throws SintaticoException, SemanticoException {
       while(Code.BEGIN != tabelaLexica.getFirst().getCode()) {
          declaracaoDeSubprograma();
          consume(Code.PONTO_E_VIRGULA);
       }
    }


    /**
     * Representa o nao-terminal declaracaoDeSubprograma.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void declaracaoDeSubprograma() throws SintaticoException, SemanticoException {
        consume(Code.PROCEDURE);
        consume(Code.ID);
        pilhaSemantica.push(new Variavel("$", 0));
        argumentos();
        consume(Code.PONTO_E_VIRGULA);
        declaracoesVariaveis();
        declaracoesDeSubprogramas();
        comandoComposto();
    }


    /**
     * Representa o nao-terminal argumentos.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void argumentos() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.ABRE_PARENTESES:
                consume(Code.ABRE_PARENTESES);     
                listaDeParametros();
                consume(Code.FECHA_PARENTESES);
                break;
            case Code.PONTO_E_VIRGULA:
                break;
        }
    }


    /**
     * Representa o nao-terminal listaDeParametros.
     *
     * @throws SintaticoException
     */
    public void listaDeParametros() throws SintaticoException, SemanticoException {
        tipo = -1;
        listaDeIdentificadores();
        consume(Code.DOIS_PONTOS);     
        tipo();
        listaDeParametros2();
    }


    /**
     * Representa o nao-terminal listaDeParametros2.
     *
     * @throws SintaticoException
     */
    public void listaDeParametros2() throws SintaticoException, SemanticoException {
        while(Code.FECHA_PARENTESES != tabelaLexica.getFirst().getCode()) {     
            tipo = -1;
            consume(Code.PONTO_E_VIRGULA);
            listaDeIdentificadores();
            consume(Code.DOIS_PONTOS);
            tipo();
        }
    }


    /**
     * Representa o nao-terminal comandoComposto.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    @SuppressWarnings("empty-statement")
    public void comandoComposto() throws SintaticoException, SemanticoException {
        ignoraID = true;
        consume(Code.BEGIN);
        comandosOpcionais();        
        consume(Code.END);
        ignoraID = false;

        while(!pilhaSemantica.pop().getId().equals("$"));

    }


    /**
     * Representa o nao-terminal comandosOpcionais.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void comandosOpcionais() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case(Code.ID):
            case(Code.BEGIN):
            case(Code.IF):
            case(Code.WHILE):
                listaDeComandos();
                break;
            case(Code.END):
                break;
        }
    }


    /**
     * Representa o nao-terminal listaDeComandos.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeComandos() throws SintaticoException, SemanticoException {
        comando();
        listaDeComandos2();
    }


    /**
     * Representa o nao-terminal listaDeComandos2.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeComandos2() throws SintaticoException, SemanticoException {
        while(Code.END != tabelaLexica.getFirst().getCode() ) {
            consume(Code.PONTO_E_VIRGULA);
            comando();
        }
    }


    /**
     * Representa o nao-terminal comando.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void comando() throws SintaticoException, SemanticoException {
         switch(tabelaLexica.getFirst().getCode()) {
            case(Code.ID):
                subComando();
                break;
            case(Code.BEGIN):
                comandoComposto();
                break;
            case(Code.IF):
                consume(Code.IF);
                operacao = Code.BOOLEAN;
                expressao();
                consume(Code.THEN);
                comando();
                operacao = Code.BOOLEAN;
                parteElse();
                break;
            case(Code.WHILE):
                consume(Code.WHILE);
                expressao();
                consume(Code.DO);
                comando();
                break;

             default:
                 throw new SintaticoException(tabelaLexica.getFirst(), tabelaLexica.getFirst().getLine());
        }
    }


    /**
     * Representa um nao-terminal para auxilio.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void subComando() throws SintaticoException, SemanticoException {
        for(int i = pilhaSemantica.size()-1; i >=0; --i ) {
            if(pilhaSemantica.get(i).getId().equals(tabelaLexica.getFirst().getSimbol())) {
                operacao = pilhaSemantica.get(i).getTipo();
                break;
            }
            else if(i == 0) {
                throw new SemanticoException(tabelaLexica.getFirst().getSimbol(), SemanticoException.ID_NAO_DECLARADO, linhaAtual);
            }
        }
        /*if(!pilhaSemantica.contains(new Variavel( tabelaLexica.getFirst().getSimbol(), tabelaLexica.getFirst().getCode() )) ) {
            
        }*/
        consume(Code.ID);

        if(tabelaLexica.getFirst().getCode() == Code.ABRE_PARENTESES) {
             consume(Code.ABRE_PARENTESES);
             listaDeExpressoes();
             consume(Code.FECHA_PARENTESES);
        }
        else if(tabelaLexica.getFirst().getCode() == Code.ATRIBUICAO) {
            consume(Code.ATRIBUICAO);
            expressao();
            if(operacao != pct.pop().getTipo()) {
                throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
            }
        }
    }


    /**
     * Representa o nao-terminal parteElse.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void parteElse() throws SintaticoException, SemanticoException {
        if(tabelaLexica.getFirst().getCode() == Code.ELSE) {
            consume(Code.ELSE);          
            comando();
        }
    }


    /**
     * Representa o nao-terminal variavel.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void variavel() throws SintaticoException, SemanticoException {
        consume(Code.ID);
    }


    /**
     * Representa o nao-terminal ativacaoDeProcedimento.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void ativacaoDeProcedimento() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.ID: 
                consume(Code.ID);
                
                if(tabelaLexica.getFirst().getCode() == Code.ABRE_PARENTESES) {
                    consume(Code.ABRE_PARENTESES);
                    listaDeExpressoes();                 
                    consume(Code.FECHA_PARENTESES);
                }  
                break;
        }
    }


    /**
     * Representa o nao-terminal listaDeExpressoes.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void listaDeExpressoes() throws SintaticoException, SemanticoException {
        expressao();
        
        if(tabelaLexica.getFirst().getCode() == Code.VIRGULA) {
            consume(Code.VIRGULA);
            listaDeExpressoes();
        }
    }


    /**
     * Representa o nao-terminal expressao.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void expressao() throws SintaticoException, SemanticoException {
        expressaoSimples();
        
        if(isOp_Relacional(tabelaLexica.getFirst().getCode())) {
            int code1, code2;
            op_relacional();
            expressaoSimples();
            code1 = pct.pop().getTipo();
            code2 = pct.pop().getTipo();
            if(code1 == Code.INTEGER && code2 == Code.INTEGER) {
                pct.push(new Variavel(null, Code.BOOLEAN));
            }
            else if(code1 == Code.REAL && (code2 == Code.INTEGER || code2 == Code.REAL)) {
                pct.push(new Variavel(null, Code.BOOLEAN));
            }
            else if(code2 == Code.REAL && (code1 == Code.INTEGER || code1 == Code.REAL)) {
                pct.push(new Variavel(null, Code.BOOLEAN));
            }
            else {
                throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
            }
        }
    }


    /**
     * Representa o nao-terminal expressaoSimples.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void expressaoSimples() throws SintaticoException, SemanticoException {

        if(isSinal(tabelaLexica.getFirst().getCode())) {
            sinal();
            termo();
            expressaoSimples2();
            if(pct.peek().tipo == Code.BOOLEAN) {
                throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
            }
        }
        else{
            termo();
            expressaoSimples2();
        }
    }


    /**
     * Representa o nao-terminal expressaoSimples2.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void expressaoSimples2() throws SintaticoException, SemanticoException {
        int code1, code2;
        if(isOp_Aditivo(tabelaLexica.getFirst().getCode())) {
            op_aditivo();
            termo();
            code1 = pct.pop().getTipo();
            code2 = pct.pop().getTipo();
            if(code1 == Code.INTEGER && code2 == Code.INTEGER) {
                if(operacao == Code.INTEGER) {
                    pct.push(new Variavel(null, Code.INTEGER));
                }
                else {
                    pct.push(new Variavel(null, Code.REAL));
                }
            }
            else if(code1 == Code.REAL && (code2 == Code.INTEGER || code2 == Code.REAL)) {
                pct.push(new Variavel(null, Code.REAL));
            }
            else if(code2 == Code.REAL && (code1 == Code.INTEGER || code1 == Code.REAL)) {
                pct.push(new Variavel(null, Code.REAL));
            }
            else if(code1 == Code.BOOLEAN && code2 == Code.BOOLEAN) {
                pct.push(new Variavel(null, Code.BOOLEAN));
            }
            else {
                throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
            }
            expressaoSimples2();
        }
    }


    /**
     * Representa o nao-terminal termo.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void termo() throws SintaticoException, SemanticoException {
        fator();
        termo2();
    }


    /**
     * Representa o nao-terminal termo2.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void termo2() throws SintaticoException, SemanticoException {
        if(isOp_Multiplicativo(tabelaLexica.getFirst().getCode())) {
            int code1, code2;

            op_multiplicativo();
            fator();
            
            code1 = pct.pop().getTipo();
            code2 = pct.pop().getTipo();
            if(code1 == Code.INTEGER && code2 == Code.INTEGER) {
                pct.push(new Variavel(null, Code.INTEGER));
            }
            else if(code1 == Code.REAL && (code2 == Code.INTEGER || code2 == Code.REAL)) {
                pct.push(new Variavel(null, Code.REAL));
            }
            else if(code2 == Code.REAL && (code1 == Code.INTEGER || code1 == Code.REAL)) {
                pct.push(new Variavel(null, Code.REAL));
            }
            else if(code1 == Code.BOOLEAN && code2 == Code.BOOLEAN) {
                pct.push(new Variavel(null, Code.BOOLEAN));
            }
            else {
                throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
            }
            termo2();
        }
    }


    /**
     * Representa o nao-terminal fator.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void fator() throws SintaticoException, SemanticoException {
        Variavel aux = null;
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.ID:

                for(int i = pilhaSemantica.size()-1; i >=0; --i ) {
                    if(pilhaSemantica.get(i).getId().equals(tabelaLexica.getFirst().getSimbol())) {
                        aux = pilhaSemantica.get(i);
                        break;
                    }
                    else if(i == 0) {
                        throw new SemanticoException(tabelaLexica.getFirst().getSimbol(), SemanticoException.ID_NAO_DECLARADO, linhaAtual);
                    }
                }

                consume(Code.ID);
                pct.push(new Variavel(aux.getId(), aux.getTipo()));
                
                if(tabelaLexica.getFirst().getCode() == Code.ABRE_PARENTESES){
                    consume(Code.ABRE_PARENTESES);
                    
                    listaDeExpressoes();
                    
                    consume(Code.FECHA_PARENTESES);
                }
                break;
            case Code.INTEIRO:
                pct.push(new Variavel(tabelaLexica.getFirst().getSimbol(), Code.INTEGER));
                consume(Code.INTEIRO);
                break;
            case Code.REAIS:
                pct.push(new Variavel(tabelaLexica.getFirst().getSimbol(), Code.REAL));
                consume(Code.REAIS);
                break;
            case Code.TRUE:
                pct.push(new Variavel(tabelaLexica.getFirst().getSimbol(), Code.BOOLEAN));
                consume(Code.TRUE);
                break;
            case Code.FALSE:
                pct.push(new Variavel(tabelaLexica.getFirst().getSimbol(), Code.BOOLEAN));
                consume(Code.FALSE);
                break;
            case Code.ABRE_PARENTESES:
                consume(Code.ABRE_PARENTESES);
                expressao();              
                consume(Code.FECHA_PARENTESES);                
                break;
            case Code.NOT:            
                consume(Code.NOT);
                fator();
                if(pct.peek().tipo != Code.BOOLEAN) {
                    throw new SemanticoException(SemanticoException.INCOMPATIBILIDADE_TIPOS, linhaAtual);
                }
                break;

            default:
                throw new SintaticoException(tabelaLexica.getFirst(), tabelaLexica.getFirst().getLine());
        }
    }


    /**
     * Representa o nao-terminal sinal.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void sinal() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.ADICAO:    
                consume(Code.ADICAO);
                break;
            case Code.SUBTRACAO:
                consume(Code.SUBTRACAO);
                break;
        }
    }


    /**
     * Metodo responsavel para saber se o code corrente e' um sinal.
     *
     * @param code
     * @return      true, se for um code.
     */
    public boolean isSinal(int code) {
        if(code == Code.ADICAO || code == Code.SUBTRACAO) {
            return true;
        }
        return false;
    }


    /**
     * Representa o nao-terminal op_relacional.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void op_relacional() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.IGUAL:       consume(Code.IGUAL);       break;
            case Code.MENOR_QUE:   consume(Code.MENOR_QUE);   break;
            case Code.MAIOR_QUE:   consume(Code.MAIOR_QUE);   break;
            case Code.MAIOR_IGUAL: consume(Code.MAIOR_IGUAL); break;
            case Code.MENOR_IGUAL: consume(Code.MENOR_IGUAL); break;
            case Code.DIFERENTE:   consume(Code.DIFERENTE);   break;
        }
    }


    /**
     * Metodo responsavel para saber se o code corrente e' um operador relacional.
     *
     * @param code
     * @return      true, se for um code.
     */
    public boolean isOp_Relacional(int code) {
        if(code == Code.IGUAL || code == Code.MENOR_QUE || code == Code.MAIOR_QUE || 
                code == Code.MAIOR_IGUAL || code == Code.MENOR_IGUAL || code == Code.DIFERENTE) {
            return true;
        }
        return false;
    }


    /**
     * Representa o nao-terminal op_aditivo.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void op_aditivo() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.ADICAO:    consume(Code.ADICAO);    break;
            case Code.SUBTRACAO: consume(Code.SUBTRACAO); break;
            case Code.OR:        consume(Code.OR);        break;
        }
    }


    /**
     * Metodo responsavel para saber se o code corrente e' um operador aditivo.
     *
     * @param code
     * @return      true, se for um code.
     */
    public boolean isOp_Aditivo(int code) {
        if(code == Code.ADICAO || code == Code.SUBTRACAO || code == Code.OR) {
            return true;
        }
        return false;
    }


    /**
     * Representa o nao-terminal op_multiplicativo.
     *
     * @throws SintaticoException   Tratara a excecao da analise sintatica.
     */
    public void op_multiplicativo() throws SintaticoException, SemanticoException {
        switch(tabelaLexica.getFirst().getCode()) {
            case Code.MULTIPLICACAO: consume(Code.MULTIPLICACAO); break;
            case Code.DIVISAO:       consume(Code.DIVISAO);       break;
            case Code.AND:           consume(Code.AND);           break;
        }
    }


    /**
     * Metodo responsavel para saber se o code corrente e' um operador multiplicativo.
     *
     * @param code
     * @return      true, se for um code.
     */
    public boolean isOp_Multiplicativo(int code) {
        if(code == Code.MULTIPLICACAO || code == Code.DIVISAO || code == Code.AND) {
            return true;
        }
        return false;
    }

    private class Variavel {

        String id;
        int tipo;

        public Variavel(String id, int tipo) {
            this.id = id;
            this.tipo = tipo;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public int getTipo() {
            return tipo;
        }

        public void setTipo(int tipo) {
            this.tipo = tipo;
        }
    }
}
